import base64
import logging
import os
import socket
import sys

from server import check_valid
from utils import load_config, server_response, send_msg, print_to_server, parse_cmd, print_to_client, is_cmd_valid, \
    commands, save_mail, generate_nonce, generate_hmac_value

PERSONAL_ID ='C8BB1A'
PERSONAL_SECRET = '4acb39586b46d8455b20ebfce57b46cd'

logging.basicConfig(format='%(asctime)s - %(pathname)s[line:%(lineno)d] - %(levelname)s: %(message)s',
                     level=logging.DEBUG)




class multi_server:

    def __init__(self,path):
        try:
            config = load_config(path)
            self.server_port = config['DEFAULT']['server_port']
            self.host = '127.0.0.1'
            self.inbox = config['DEFAULT']['inbox_path']
        except Exception:
            exit(2)
        self.name = PERSONAL_ID
        self.secret = PERSONAL_SECRET
        self.auth = False
        self.run_multi()

    def _multi_server_(self,connection,pid,order):
        send_msg(connection,server_response.re_220.value)
        print_to_server(server_response.re_220.value,True,pid,order)
        state =[]
        mail_buffer = []
        while True:
            try:
                response = connection.recv(1024).decode()
                if not response:
                    print_to_server(msg="Connection lost",multi=True,pid=pid,order=order)
            except Exception:
                print_to_server(msg="Connection lost",multi=True,pid=pid,order=order)
                break
            cmd,parameter,res = parse_cmd(response,state[-1] if len(state) else None)
            print_to_client(response,True,pid,order)
            try:
                # 先检查 语句状态在检查 是否合法?
                if not check_valid(state, cmd):
                    send_msg(connection, server_response.re_503.value)
                    print_to_server(server_response.re_503.value,True,pid,order)
                    continue

                if not is_cmd_valid(cmd, parameter, response):
                    send_msg(connection, server_response.re_501.value)
                    print_to_server(server_response.re_501.value,True,pid,order)
                    state.pop(-1)
                    continue
                if cmd == commands.EHLO.value:
                    send_msg(connection, f"250 {self.host}\r\n250 {commands.AUTH.value} CRAM-MD5")
                    print_to_server(f"250 {self.host}",True,pid,order)
                    print_to_server(f"250 {commands.AUTH.value} CRAM-MD5",True,pid,order)
                elif cmd == commands.MAIL.value:
                    mail_buffer.append((cmd, parameter))
                    send_msg(connection, server_response.re_250.value)
                    print_to_server(server_response.re_250.value,True,pid,order)

                elif cmd == commands.RCPT.value:
                    mail_buffer.append((cmd, parameter))
                    send_msg(connection, server_response.re_250.value)
                    print_to_server(server_response.re_250.value,True,pid,order)
                elif cmd == commands.DATA.value:
                    send_msg(connection, server_response.re_354.value)
                    print_to_server(server_response.re_354.value,True,pid,order)
                elif cmd == commands.DATA_BODY.value:
                    mail_buffer.append((cmd, parameter))
                    send_msg(connection, server_response.re_354.value)
                    print_to_server(server_response.re_354.value,True,pid,order)

                elif cmd == commands.DATA_END.value:
                    send_msg(connection, server_response.re_250.value)
                    print_to_server(server_response.re_250.value,True,pid,order)

                elif cmd == commands.REST.value:
                    mail_buffer.clear()
                    state.clear()
                    state.append(commands.REST.value)
                    send_msg(connection, server_response.re_250.value)
                    print_to_server(server_response.re_250.value,True,pid,order)  # 补上Rest 的返回值 25,pid,order0
                elif cmd == commands.QUIT.value:
                    if len(state) > 2 and state[-2] == commands.DATA_END.value:  # 如果是QUIT指令,且此时前一位是END时候,发送邮件
                        save_mail(self.inbox, mail_buffer,multi=True,pid=pid,order=order)
                    mail_buffer.clear()
                    state.clear()
                    send_msg(connection, server_response.re_221.value)
                    print_to_server(server_response.re_221.value,True,pid,order)
                    break
                elif cmd == commands.AUTH.value:
                    if parameter != "CRAM-MD5":
                        print_to_server(server_response.re_504.value,True,pid,order)
                        send_msg(connection, server_response.re_504.value)
                        state.pop(-1)
                    else:
                        self.nonce = generate_nonce()
                        send_msg(connection, f"{server_response.re_334.value} {self.nonce.decode()}")
                        print_to_server(f"{server_response.re_334.value} {self.nonce.decode('utf-8')}",True,pid,order)
                elif cmd == commands.AUTH_CHALLENGE.value:
                    challenge = parameter
                    if parameter == '*':
                        send_msg(connection, server_response.re_501.value)
                        print_to_server(server_response.re_501.value,True,pid,order)
                        continue
                    try:
                        ans = base64.b64decode(challenge.encode()).decode('utf-8').split()
                    except Exception as e:
                        send_msg(connection, server_response.re_501.value)
                        print_to_server(server_response.re_501.value,True,pid,order)
                        continue
                    if generate_hmac_value(self.secret, self.nonce) == ans[1]:
                        send_msg(connection, server_response.re_235.value)
                        print_to_server(server_response.re_235.value,True,pid,order)
                    else:
                        send_msg(connection, server_response.re_535.value)
                        print_to_server(server_response.re_535.value,True,pid,order)

                elif cmd == commands.NOOP.value:
                    if len(state) and state[-1] == commands.NOOP.value: state.pop(-1)
                    send_msg(connection, server_response.re_250.value)
                    print_to_server(server_response.re_250.value,True,pid,order)
            except BrokenPipeError:
                print_to_server("Connection lost")
                break

    def run_multi(self):

        with socket.socket(socket.AF_INET,socket.SOCK_STREAM) as server:
            server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
            server.bind((self.host,int(self.server_port)))
            num_client = 1
            while True:
                server.listen()
                connection,addr = server.accept()
                child = os.fork()
                if child == 0: # 子进程
                    self._multi_server_(connection,os.getpid(),order=num_client)
                else:
                    num_client+=1











def main(argv):
    multi_server(argv[1])

if __name__ == '__main__':
    if (len(sys.argv))<2 :
        exit(1)
    main(sys.argv)